#include <QDateTime>
#include <QHostAddress>
#include "ttgipsprotocol.h"
#include "define_cmd.h"

static unsigned char entable[] = {
    136, 187,  117 , 111 , 131 , 144 , 188 , 54 , 204 , 37, //10
    102 , 73 , 11 , 1  ,165 , 100 , 190  ,149 , 91 , 27 , //20
    249 , 179 , 184 , 252 , 246 , 71 , 224 , 225 , 69 , 77,//30
    106 , 15 , 214  ,240 , 98 , 132 , 128 , 61 , 55 , 12, //40
    220 , 49 , 44 , 153 , 94 , 138 , 56 , 139 , 185 , 16,  //50
    70 , 39 , 191 , 221 , 235,  81,  196 , 123 , 101 , 119,//60
    241 , 168 , 90 , 72 , 216 , 134 , 57 , 30 , 174 , 0 , //70
    195 , 28 , 147 , 229 , 143 , 50 , 170 , 112 , 199,  2,  //80
    86 , 254 , 114 , 141 , 180 , 228 , 40 , 74 , 62 , 193 ,//90
    205,  95 , 151 , 75 , 155 , 208 , 36 , 76 , 183 , 85 , //100
    8 , 59 , 5 , 175 , 223 , 93 , 13 , 122 , 137 , 247,  //110
    201 , 242 , 211 , 26 , 89 , 53 , 126 , 145 , 125 , 92, //120
    51 , 167 , 42 , 64 , 116 , 105 , 173 , 7 , 34 , 97 , //130
    157 , 46 , 96 , 233 , 200 , 161 , 23 , 129 , 253 , 133 ,//140
    232 , 255,  103 , 230 , 78 , 67 , 218 , 163 , 19 , 140  ,//150
    109 , 33 , 194 , 226 , 80 , 231 , 213 , 6 , 79 , 48,  //160
    162 , 243 , 238 , 87 , 222 , 172 , 14 , 38 , 186 , 212 , //170
    154 , 63 , 22 , 52,  245 , 164 , 160 , 31 , 4 , 198 , //180
    239 , 219 , 248 , 43 , 88 , 227 , 32 , 197 , 24 , 234 , //190
    108 , 192 , 113 , 210 , 120 , 177 , 83 , 207 , 118 , 20 , //200
    25 , 104 , 215 , 159 , 124 , 130 , 244 , 115 , 60 , 68 , //210
    181 , 121 , 135 , 127 , 217 , 182 , 206 , 176 , 45 , 110 ,//220
    158 , 171 , 150 , 166 , 65 , 152 , 17 , 203 , 84 , 178  ,//230
    189 , 146 , 3 , 237 , 99  ,107 , 47 , 29 , 21 , 251 , //240
    209 , 156 , 148 , 9,  10, 18 , 35 , 66 , 236 , 82 , //250
    41 , 142 , 202 , 169 , 250 , 58 //256

};

const unsigned char enTable2[] ={
220 , 211 , 135 , 222 , 33 , 10 , 179 , 45 , 238 , 75 , //10
103 , 225 , 28 , 101,  204 , 72 , 125 , 63 , 16 , 36 , //20
248 , 73 , 26 , 11 , 129 , 207 , 84 , 91 , 147 , 230 , //30
164 , 177 , 21 , 192 , 212 ,152 , 44 , 159 , 233 , 189, //40
234 , 110 , 209 , 119 , 23 , 244 , 58 , 141 , 83 , 190 ,//50
245 , 167 , 120 , 19 , 178 , 187 , 151 , 219 , 149 , 243,  //60
255 , 172 , 56 , 86 ,  183 , 17 ,  27,  246 , 171 ,  7 , //70
226 , 98 ,  8 ,  9  , 132 ,  4 ,  29 , 59 ,  20 , 46 , //80
32 , 1 , 156 , 112 , 176 , 52 , 130 , 239 , 2 , 71 , //90
54 , 49 , 65 , 107 , 14 , 155 , 87 , 22 , 140 , 42 , //100
165 , 24 , 116 , 170 , 229 , 198 , 108 , 12 , 139 , 131 , //110
127 , 251 , 25 , 182 , 61 , 94 , 194 , 37 , 55 , 48 , //120
69 , 74 , 99 , 34 , 254 , 213 , 117 , 62 , 163 , 161 , //130
123 , 157 , 3 , 51 , 223 , 180 , 104 , 153 , 118 , 113,  //140
47 , 96 , 193 , 142 , 236 , 41 , 106 , 102 , 88 , 228 , //150
232 , 166 , 121 , 216 , 35 , 185 , 224 , 85 , 196 , 253 ,//160
186 , 162 , 200 , 15 , 143 , 215 , 13 , 146 , 206 , 221 , //170
68 , 60 , 199 , 168 , 235,  90 , 158  ,175 , 138 , 227 , //180
148 , 154 , 105,  181,  128,  237,  202 , 82,  97 , 39 , //190
30 , 160 , 231 , 70 , 76 , 5 , 81 , 242 , 0 , 89 , //200
50 , 250 , 214 , 43 , 252 , 197 , 205 , 137 , 144 , 40 , //210
64 , 115 , 79 , 210 , 92 , 124 , 66 , 240 , 188 , 174 , //220
217 , 169 , 150,  136 , 247,  18 , 145 , 38,  208 , 203 , //230
57 , 184 , 67 , 77 , 78 , 114 , 126 , 31 , 241 , 218, //240
109 , 134 , 133 , 93 , 249 , 195 , 191 , 173 , 95 , 201 , //250
100 , 80 , 53 , 6 , 122 , 111                       // 256
};

//format AAAA-LLLL-CCCCCCCC
QByteArray TTGIpsProtocol::phyStringToBytes(QString phyAddr)
{
    _u32 address[3] = {0},mask = 0xf,mainMask=0xf;
    char addr_msg[2] = {0};

    QStringList list = phyAddr.split(".");
    if(list.size() == 3)
    {
        address[0] = list.at(0).toUInt();
        address[1] = list.at(1).toUInt();
        address[2] = list.at(2).toUInt();
    }
    else
    {
        qDebug() << "phyStringToBytes :  address not correct " << phyAddr;
    }


    addr_msg[0] = (_u8)(((address[0]&mainMask)<<4)|(address[1]&mask));     // dest address hi byte
    addr_msg[1] = (_u8)(address[2]&0xff);     // dest address lo byte

    return QByteArray(addr_msg,2);
}

//format AAA-LLLLL-CCCCCCCC
QByteArray TTGIpsProtocol::groupStringToBytes(QString gAddr)
{
    _u32 address[3] = {0},mask = 0x7,mainMask=0x1f;
    char addr_msg[2] = {0};

    QStringList list = gAddr.split("/");
    if(list.size() == 3)
    {
        address[0] = list.at(0).toUInt();
        address[1] = list.at(1).toUInt();
        address[2] = list.at(2).toUInt();
    }
    else
    {
        qDebug() << "groupStringToBytes :  address not correct " << gAddr;
    }

    addr_msg[0] = (_u8)(((address[0]&mainMask)<<3)|(address[1]&mask));     // dest address hi byte
    addr_msg[1] = (_u8)(address[2]&0xff);     // dest address lo byte

    return QByteArray(addr_msg,2);
}

int TTGIpsProtocol::second_round_encry(unsigned char *send, int length, unsigned char *encrybuffer)
{
    if(length<3 || send[0] != 0xAA || send[1] != 0x55)
        return 0;

    unsigned int seed  = QDateTime::currentDateTime().toTime_t();
    qsrand(seed);

    int random = qrand()&0xff;
    int E = enTable2[random]&0xff;
    int temp;

    encrybuffer[0] = 0xAA;
    encrybuffer[1] = 0x55;

    //从长度开始到结尾，整个加密
    for(int index =2;index<length;index++)
    {
        if(index%2 == 0)
            temp = send[index]+index;
        else
            temp = send[index]-index;

        encrybuffer[index] = (temp&0xff)^ E ;
    }

    //末尾存放随机数索引
     encrybuffer[length] = random;

     return length +1 ;
}

int TTGIpsProtocol::second_round_decry(unsigned char *recv,int length,unsigned char *decrybuffer)
{
    if(length<3 || recv[0] != 0xAA || recv[1] != 0x55)
        return 0;

    decrybuffer[0] = 0xAA;
    decrybuffer[1] = 0x55;

    unsigned int random = recv[length-1] & 0xff;
    int E = enTable2[random]&0xff;
    int temp;

    //末尾的随机数不需要转换
    for(int index =2;index<length-1;index++)
    {
        temp = recv[index]^E;

        if(index%2 == 0)
            decrybuffer[index] = (temp - index)&0xff;
        else
            decrybuffer[index] = (temp + index)&0xff;
    }

    return length-1;
}

int TTGIpsProtocol::ipad_data_decry(unsigned char *cemi_data, int size, unsigned char *decode_buf)
{
    memset(decode_buf, 0, size);

    int index = cemi_data[size-1]&0xff;
    int r = entable[index];

    int cemi_size = size-1;
    for(unsigned int i=0;i<cemi_size;i++)
    {
        decode_buf[i] = cemi_data[cemi_size - 1 - i];
        decode_buf[i] = decode_buf[i] ^ r;
    }

    return cemi_size;
}

TTGIpsProtocol::TTGIpsProtocol(QObject *parent) :
    QObject(parent)
{
    _packCRC = new CPackageCRC();
}

void TTGIpsProtocol::setIpsPassword(QString pass)
{
    if(pass.size()!=6)
    {
        qDebug() <<"size of password not 6 bytes!";
        return ;
    }

    QByteArray tmpPass = pass.toLocal8Bit();
    memcpy(_password,tmpPass.data(),6);

}

QByteArray TTGIpsProtocol::decryPassword(QByteArray oriData)
{
    char d[6] = {0},e[7]={0};

    if(oriData.size()>INDEX_OF_CMD)
    {
        memcpy(e,oriData.data()+INDEX_OF_PASS,7);
        for(int i=0;i<6;i++)
        {
            d[i] = e[5-i] ^ e[6];
        }
    }

    //do not need the random number
    return QByteArray(d,6);
}

QByteArray TTGIpsProtocol::generateEncryPassword(char *pass)
{
    static int table_size = sizeof(entable);
    char psw[7] = {0};

    char *usePass = NULL;
    if(pass!=NULL)
        usePass = pass;
    else
        usePass = (char *)_password;

    static uint seed = 0;
    if(seed == 0)
        seed = QDateTime::currentDateTime().toTime_t();
    else
        seed = (seed * QTime::currentTime().second()) & 0xffff;

    qsrand(seed);
    _random = qrand()%(table_size);


    for(int i=0;i<6;i++)
    {
        psw[i] = usePass[5-i];
        psw[i] = psw[i] ^ entable[_random];
    }

    psw[6] = _random;

    return QByteArray(psw,7);
}

QByteArray TTGIpsProtocol::msgHeader()
{
    //0xAA	0x55	长度-2byte	密码区-7byte
    static char header[11];

    memset(header,0,sizeof(header));

    header[0] = 0xAA;
    header[1] = 0x55;
    header[2] = 0x0;
    header[3] = 0x0;

    QByteArray pass = generateEncryPassword();
    memcpy(&header[4],pass.data(),7);

    return QByteArray(header,11);
}

//长度为密码区开始到CRC2结束的byte总数
void TTGIpsProtocol::setPackageSize(char *pdata,int size)
{
    int length = size - INDEX_OF_PASS;

    pdata[INDEX_OF_SIZE] = (length>>8) & 0xff;
    pdata[INDEX_OF_SIZE+1] = length & 0xff;
}

unsigned short TTGIpsProtocol::getCmdCode(QByteArray oriData)
{
    unsigned short cmd = 0;

    if(oriData.size()>INDEX_OF_DATA)
    {
        char *d = oriData.data();
        cmd = (d[INDEX_OF_CMD] << 8)|d[INDEX_OF_CMD+1];
    }

    return cmd;
}

void TTGIpsProtocol::ipSwapTobigEndian(char *source,char *dest)
{
    dest[0] = source[3];
    dest[1] = source[2];
    dest[2] = source[1];
    dest[3] = source[0];
}

QByteArray TTGIpsProtocol::boardcastPackage()
{
    short cmd = CMD_BOARDCAST_DEVICE;
    QByteArray data = msgHeader();

    data.append(BYTE_HI(cmd));
    data.append(BYTE_LOW(cmd));

    setPackageSize(data.data(),data.size()+2);
    //no context data
    _u16 crc = CPackageCRC::crc_cal_by_byte((unsigned char*)(data.data()+2),data.size()-2);

    data.append(BYTE_HI(crc));
    data.append(BYTE_LOW(crc));

    return data;
}

QByteArray TTGIpsProtocol::searchIpPackage(unsigned int ipInt)
{
    short cmd = CMD_IP_USED_REQ;
    char ipBuf[4] = {0};
    char *p = (char *)&ipInt;

    ipSwapTobigEndian(p,ipBuf);

    QByteArray data = msgHeader();

    data.append(BYTE_HI(cmd));
    data.append(BYTE_LOW(cmd));
    data.append(ipBuf,4);

    setPackageSize(data.data(),data.size()+2);
    //no context data
    _u16 crc = CPackageCRC::crc_cal_by_byte((unsigned char*)(data.data()+2),data.size()-2);

    data.append(BYTE_HI(crc));
    data.append(BYTE_LOW(crc));



    return data;
}

QByteArray TTGIpsProtocol::devIpChange(unsigned int ipInt)
{
    short cmd = CMD_IP_CHANGE_REQ;
    char ipBuf[4] = {0};
    char *p = (char *)&ipInt;

    ipSwapTobigEndian(p,ipBuf);

    QByteArray data = msgHeader();

    data.append(BYTE_HI(cmd));
    data.append(BYTE_LOW(cmd));
    data.append(ipBuf,4);

    setPackageSize(data.data(),data.size()+2);

    _u16 crc = CPackageCRC::crc_cal_by_byte((unsigned char*)(data.data()+2),data.size()-2);

    data.append(BYTE_HI(crc));
    data.append(BYTE_LOW(crc));

    return data;
}

QByteArray TTGIpsProtocol::binFileHand()
{
    short cmd = CMD_UPLOAD_HAND;

    QByteArray data = msgHeader();

    data.append(BYTE_HI(cmd));
    data.append(BYTE_LOW(cmd));

    setPackageSize(data.data(),data.size()+2);

    _u16 crc = CPackageCRC::crc_cal_by_byte((unsigned char*)(data.data()+2),data.size()-2);

    data.append(BYTE_HI(crc));
    data.append(BYTE_LOW(crc));

    return data;
}

QByteArray TTGIpsProtocol::binFileTansfer(char total, char index, QByteArray contextData)
{
    short cmd = CMD_UPLOAD_SEND;

    QByteArray data = msgHeader();

    data.append(BYTE_HI(cmd));
    data.append(BYTE_LOW(cmd));

    data.append(total);
    data.append(index);
    data.append(contextData);

    setPackageSize(data.data(),data.size()+2);

    _u16 crc = CPackageCRC::crc_cal_by_byte((unsigned char*)(data.data()+2),data.size()-2);

    data.append(BYTE_HI(crc));
    data.append(BYTE_LOW(crc));

    return data;
}


QByteArray TTGIpsProtocol::passwordInitPackage(QByteArray pass)
{
    short cmd = CMD_PASS_INIT;

    QByteArray data = msgHeader();

    data.append(BYTE_HI(cmd));
    data.append(BYTE_LOW(cmd));

    if(pass.size()!=6)
        pass.resize(6);

    QByteArray encyPass = generateEncryPassword(pass.data());
    data.append(encyPass);

    setPackageSize(data.data(),data.size()+2);

    _u16 crc = CPackageCRC::crc_cal_by_byte((unsigned char*)(data.data()+2),data.size()-2);

    data.append(BYTE_HI(crc));
    data.append(BYTE_LOW(crc));

    return data;
}

QByteArray TTGIpsProtocol::filterGrpAddrPackage(short ucmd,QStringList *addList,char canPass,short total)
{
    short cmd = ucmd;
    short currentSize = (short)addList->size();
    QByteArray data = msgHeader();

    data.append(BYTE_HI(cmd));
    data.append(BYTE_LOW(cmd));

    // 5 bytes
    data.append(canPass);
    data.append(BYTE_HI(total));
    data.append(BYTE_LOW(total));
    data.append(BYTE_HI(currentSize));
    data.append(BYTE_LOW(currentSize));

    QByteArray listData;

    for(int i=0;i<addList->size();i++)
    {
        listData.append(groupStringToBytes(addList->at(i)));
    }

    data.append(listData);

    setPackageSize(data.data(),data.size()+2);

    _u16 crc = _packCRC->crc_cal_by_byte((unsigned char*)(data.data()+2),data.size()-2);

    data.append(BYTE_HI(crc));
    data.append(BYTE_LOW(crc));

    return data;
}

QByteArray TTGIpsProtocol::filterPhyAddrPackage(short ucmd, QStringList *addList, char canPass, short total)
{
    short cmd = ucmd;
    short currentSize = (short)addList->size();
    QByteArray data = msgHeader();

    data.append(BYTE_HI(cmd));
    data.append(BYTE_LOW(cmd));

    //////////////////////////////////////////////
    // 5 bytes
    data.append(canPass);
    data.append(BYTE_HI(total));
    data.append(BYTE_LOW(total));
    data.append(BYTE_HI(currentSize));
    data.append(BYTE_LOW(currentSize));

    QByteArray listData;

    for(int i=0;i<addList->size();i++)
    {
        listData.append(phyStringToBytes(addList->at(i)));
    }

    data.append(listData);

    setPackageSize(data.data(),data.size()+2);

    _u16 crc = _packCRC->crc_cal_by_byte((unsigned char*)(data.data()+2),data.size()-2);

    data.append(BYTE_HI(crc));
    data.append(BYTE_LOW(crc));

    return data;
}

QByteArray TTGIpsProtocol::filterIpPackage(QStringList *addList, char canPass, short total)
{
    short cmd = NETWORK2BUS_IPADDR_REQ;
    short currentSize = (short)addList->size();
    char ipBuf[4] = {0};

    QByteArray data = msgHeader();

    data.append(BYTE_HI(cmd));
    data.append(BYTE_LOW(cmd));

    //////////////////////////////////////////////
    // 5 bytes
    data.append(canPass);
    data.append(BYTE_HI(total));
    data.append(BYTE_LOW(total));
    data.append(BYTE_HI(currentSize));
    data.append(BYTE_LOW(currentSize));

    QString ip = NULL;
    unsigned int ipInt = 0;
    char *p = NULL;

    for(int i=0;i<addList->size();i++)
    {
        ip = addList->at(i);
        ipInt = QHostAddress(ip).toIPv4Address();
        p = (char *)&ipInt;
        ipSwapTobigEndian(p,ipBuf);

        data.append(ipBuf,4);
    }

    setPackageSize(data.data(),data.size()+2);

    _u16 crc = _packCRC->crc_cal_by_byte((unsigned char*)(data.data()+2),data.size()-2);

    data.append(BYTE_HI(crc));
    data.append(BYTE_LOW(crc));

    return data;
}

QByteArray TTGIpsProtocol::devPassChangePackage(QByteArray newPsw)
{
    short cmd = CMD_PASS_CHANGE;

    QByteArray data = msgHeader();

    data.append(BYTE_HI(cmd));
    data.append(BYTE_LOW(cmd));

    if(newPsw.size()!=6)
        newPsw.resize(6);

    QByteArray newPassData = generateEncryPassword(newPsw.data());
    data.append(newPassData);

    setPackageSize(data.data(),data.size()+2);

    _u16 crc = _packCRC->crc_cal_by_byte((unsigned char*)(data.data()+2),data.size()-2);

    data.append(BYTE_HI(crc));
    data.append(BYTE_LOW(crc));

    return data;
}

QByteArray TTGIpsProtocol::devSetIpadUser(int scmd, QByteArray user)
{
    short cmd = scmd;

    QByteArray data = msgHeader();

    data.append(BYTE_HI(cmd));
    data.append(BYTE_LOW(cmd));

    if((user).size()!=8)
        (user).resize(8);
    data.append(user);

    setPackageSize(data.data(),data.size()+2);

    _u16 crc = _packCRC->crc_cal_by_byte((unsigned char*)(data.data()+2),data.size()-2);

    data.append(BYTE_HI(crc));
    data.append(BYTE_LOW(crc));

    return data;
}


QByteArray TTGIpsProtocol::devSetIpadPass(int scmd,QByteArray newPsw)
{
    short cmd = scmd;

    QByteArray data = msgHeader();

    data.append(BYTE_HI(cmd));
    data.append(BYTE_LOW(cmd));

    if(newPsw.size()!=6)
        newPsw.resize(6);

    data.append(newPsw);

    setPackageSize(data.data(),data.size()+2);

    _u16 crc = _packCRC->crc_cal_by_byte((unsigned char*)(data.data()+2),data.size()-2);

    data.append(BYTE_HI(crc));
    data.append(BYTE_LOW(crc));

    return data;
}


QByteArray TTGIpsProtocol::devSetIpadPort(int scmd, QString port)
{
    short cmd = scmd;

    QByteArray data = msgHeader();

    data.append(BYTE_HI(cmd));
    data.append(BYTE_LOW(cmd));

    short portNum = port.toUShort();
    data.append(BYTE_HI(portNum));
    data.append(BYTE_LOW(portNum));

    setPackageSize(data.data(),data.size()+2);

    _u16 crc = _packCRC->crc_cal_by_byte((unsigned char*)(data.data()+2),data.size()-2);

    data.append(BYTE_HI(crc));
    data.append(BYTE_LOW(crc));

    return data;
}

QByteArray TTGIpsProtocol::devSetIpadIp(QString ip)
{
    short cmd = CMD_IPAD_IP_CHANGE;

    QByteArray data = msgHeader();

    data.append(BYTE_HI(cmd));
    data.append(BYTE_LOW(cmd));

    unsigned int ipNum = QHostAddress(ip).toIPv4Address();

    data.append(QT_IP_BYTE_0(ipNum));
    data.append(QT_IP_BYTE_1(ipNum));
    data.append(QT_IP_BYTE_2(ipNum));
    data.append(QT_IP_BYTE_3(ipNum));

    setPackageSize(data.data(),data.size()+2);

    _u16 crc = _packCRC->crc_cal_by_byte((unsigned char*)(data.data()+2),data.size()-2);

    data.append(BYTE_HI(crc));
    data.append(BYTE_LOW(crc));

    return data;
}

QByteArray TTGIpsProtocol::devSetIpadNetwork(QByteArray mac, unsigned int mask, unsigned int gateway)
{
    short cmd = CMD_IPAD_NETWORK_REQ;

    QByteArray data = msgHeader();

    data.append(BYTE_HI(cmd));
    data.append(BYTE_LOW(cmd));

    data.append(mac);

    data.append(QT_IP_BYTE_0(mask));
    data.append(QT_IP_BYTE_1(mask));
    data.append(QT_IP_BYTE_2(mask));
    data.append(QT_IP_BYTE_3(mask));

    data.append(QT_IP_BYTE_0(gateway));
    data.append(QT_IP_BYTE_1(gateway));
    data.append(QT_IP_BYTE_2(gateway));
    data.append(QT_IP_BYTE_3(gateway));

    setPackageSize(data.data(),data.size()+2);

    _u16 crc = _packCRC->crc_cal_by_byte((unsigned char*)(data.data()+2),data.size()-2);

    data.append(BYTE_HI(crc));
    data.append(BYTE_LOW(crc));

    return data;
}


QByteArray TTGIpsProtocol::devReadIpadNetwork()
{
    short cmd = CMD_IPAD_NETWORK_READ;

    QByteArray data = msgHeader();

    data.append(BYTE_HI(cmd));
    data.append(BYTE_LOW(cmd));

    setPackageSize(data.data(),data.size()+2);

    _u16 crc = _packCRC->crc_cal_by_byte((unsigned char*)(data.data()+2),data.size()-2);

    data.append(BYTE_HI(crc));
    data.append(BYTE_LOW(crc));

    return data;
}
